﻿using System.Windows.Forms;
using System.Drawing;

namespace CustomControls
{
    public class XButton : Control
    {
        public enum ButtonStyle { ImageButton, GradientButton }
        public enum ButtonBehavior { Normal, Checkbox, Radio }
        public enum ButtonState { Normal, Pressed, Stucked }
        Image backgroundImage, pressedImage;
        Bitmap bmpOffscreen;
        Color GradientColor;

        ButtonStyle Style;
        public ButtonBehavior Behavior { get; set; }
        public ButtonState State { get; set; }

        public bool Border { get; set; }
        public Color BorderColor { get; set; }

        public XButton(Point position, Image bgImage, Image prImage)
        {
            this.Style = ButtonStyle.ImageButton;
            this.Location = position;
            this.backgroundImage = bgImage;
            this.pressedImage = prImage;
            this.Width = bgImage.Width;
            this.Height = bgImage.Height;

            this.BringToFront();
        }

        public XButton(Image bgImage, Image prImage)
        {
            this.Style = ButtonStyle.ImageButton;

            this.backgroundImage = bgImage;
            this.pressedImage = prImage;
            this.Width = bgImage.Width;
            this.Height = bgImage.Height;

            this.BringToFront();
        }

        public XButton(Size size, string text, Color color)
        {
            this.backgroundImage = new Bitmap(Width, Height);
            this.pressedImage = new Bitmap(Width, Height);
            this.Style = ButtonStyle.GradientButton;
            this.GradientColor = color;
            this.Text = text;
            this.Size = size;

            FillGradient();
        }

        // very simple, not implement gamma correction or stuff like that
        private Color Brighten(Color colorBase, int amount)
        {
            int r = colorBase.R + amount;
            int g = colorBase.G + amount;
            int b = colorBase.B + amount;
            if (r > 255) r = 255;
            if (g > 255) g = 255;
            if (b > 255) b = 255;
            return Color.FromArgb(r, g, b);
        }

        private void FillGradient()
        {
            if (backgroundImage.Size != this.ClientSize)
            {
                backgroundImage.Dispose();
                backgroundImage = new Bitmap(ClientSize.Width, ClientSize.Height);
            }

            if (pressedImage.Size != this.ClientSize)
            {
                pressedImage.Dispose();
                pressedImage = new Bitmap(ClientSize.Width, ClientSize.Height);
            }

            Graphics g = Graphics.FromImage(this.backgroundImage);
            g.Clear(this.GradientColor);
            Microsoft.Drawing.GradientFill.Fill(g, new Rectangle(0, 0, this.ClientSize.Width, (int)(this.ClientSize.Height / 2)), Brighten(this.GradientColor, 40), Brighten(this.GradientColor, 20), Microsoft.Drawing.FillDirection.TopToBottom);
            g.Dispose();

            g = Graphics.FromImage(this.pressedImage);
            g.Clear(this.GradientColor);
            Microsoft.Drawing.GradientFill.Fill(g, new Rectangle(0, 0, this.ClientSize.Width, (int)(this.ClientSize.Height / 2)), this.GradientColor, Brighten(this.GradientColor, 20), Microsoft.Drawing.FillDirection.TopToBottom);
            g.Dispose();
        }

        // When the mouse button is pressed, set the "pressed" flag to true 
        // and invalidate the form to cause a repaint.  The .NET Compact Framework 
        // sets the mouse capture automatically.
        protected override void OnMouseDown(MouseEventArgs e)
        {
            if (this.State == ButtonState.Stucked && this.Behavior != ButtonBehavior.Radio)
            {
                this.State = ButtonState.Normal;
            }
            else
            {
                this.State = ButtonState.Pressed;
                base.OnMouseDown(e);
            }
            this.Invalidate();
        }

        // When the mouse is released, reset the "pressed" flag 
        // and invalidate to redraw the button in the unpressed state.
        protected override void OnMouseUp(MouseEventArgs e)
        {
            if (this.Behavior == ButtonBehavior.Normal || this.State == ButtonState.Stucked)
                this.State = ButtonState.Normal;
            else if (this.State == ButtonState.Pressed)
                this.State = ButtonState.Stucked;
            this.Invalidate();
            base.OnMouseUp(e);
        }

        protected override void OnResize(System.EventArgs e)
        {
            base.OnResize(e);
            switch (Style)
            {
                case ButtonStyle.GradientButton:
                    FillGradient();
                    break;
                default:
                    break;
            }
            this.Invalidate();
        }

        protected override void OnPaintBackground(PaintEventArgs e)
        {
            // do nothing
        }

        // Override the OnPaint method to draw the background image and the text.
        protected override void OnPaint(PaintEventArgs e)
        {
            if (bmpOffscreen == null)
            {
                bmpOffscreen = new Bitmap(this.ClientSize.Width, this.ClientSize.Height);
            }
            else if (bmpOffscreen.Size != this.ClientSize)
            {
                bmpOffscreen.Dispose();
                bmpOffscreen = new Bitmap(this.ClientSize.Width, this.ClientSize.Height);
            }

            Graphics gxOff = Graphics.FromImage(bmpOffscreen);
            gxOff.Clear(this.BackColor);

            if (this.State != ButtonState.Normal && this.pressedImage != null)
                gxOff.DrawImage(this.pressedImage, 0, 0);
            else
                gxOff.DrawImage(this.backgroundImage, 0, 0);

            // Draw the text if there is any.
            if (this.Text.Length > 0)
            {
                SizeF size = e.Graphics.MeasureString(this.Text, this.Font);

                // Center the text inside the client area of the PictureButton
                if (State == ButtonState.Normal)
                    gxOff.DrawString(this.Text, this.Font, new SolidBrush(this.ForeColor), (this.ClientSize.Width - size.Width) / 2, (this.ClientSize.Height - size.Height) / 2);
                else
                    gxOff.DrawString(this.Text, this.Font, new SolidBrush(this.ForeColor), (this.ClientSize.Width - size.Width) / 2 + 2, (this.ClientSize.Height - size.Height) / 2 + 2);
            }

            // Draw a border around the outside of the 
            // control to look like Pocket PC buttons.
            if (Border && BorderColor != null)
            {
                gxOff.DrawRectangle(new Pen(BorderColor), 0, 0, this.ClientSize.Width - 1, this.ClientSize.Height - 1);
            }
            e.Graphics.DrawImage(bmpOffscreen, 0, 0);

            base.OnPaint(e);
        }

        public void Unstuck()
        {
            State = ButtonState.Normal;
            Invalidate();
        }
    }
}